//
// Created by hay boy on 2022/7/4.
//

#ifndef ALGORITHMS_ARRAYQUEUE_H
#define ALGORITHMS_ARRAYQUEUE_H
#include "Queue.h"
#include <sstream>
#include "../exception/illegalParameterValue.h"
#include "QueueException.h"
using namespace std;

template<class T>
class ArrayQueue : public Queue<T> {
public:
    ArrayQueue(int initialCapacity = 10);
    ~ArrayQueue(){
        delete[] queue;
    }
    bool empty() const{
        return theFront == theBack;
    }
    int size() const{
        return (theBack-theFront+arrayLength)%arrayLength;
    }
    T& front(){
        if(theFront==theBack){
            throw QueueEmpty();
        }
        return queue[(theFront+1)%arrayLength];
    }
    T& back(){
        if(theFront==theBack){
            throw QueueEmpty();
        }
        return queue[theBack];
    }
    void pop(){
        if(theFront==theBack){
            throw QueueEmpty();
        }
        theFront = (theFront+1)%arrayLength;
        queue[theFront].~T();
    }
    void push(const T& theElement);
private:
    int theFront;
    int theBack;
    int arrayLength;
    T* queue;
};

template<class T>
ArrayQueue<T>::ArrayQueue(int initialCapacity) {
    if(initialCapacity<1){
        ostringstream s;
        s<<"Initial capacity = "<<initialCapacity<<" Must be > 0 ";
        throw illegalParameterValue(s.str());
    }
    arrayLength = initialCapacity;
    queue = new T[arrayLength];
    theFront = 0;
    theBack = 0;
}

template<class T>
void ArrayQueue<T>::push(const T &theElement) {
    if((theBack+1)%arrayLength==theFront){
        T* newQueue = new T[2*arrayLength];
        int start = (theFront+1)%arrayLength;
        if(start<2){
            copy(queue+start,queue+start+arrayLength-1,newQueue);
        }else{
            copy(queue+start,queue+arrayLength,newQueue);
            copy(queue,queue+theBack+1,newQueue+arrayLength-start);
        }
        theFront = 2 * arrayLength - 1;
        theBack = arrayLength - 2;
        arrayLength *= 2;
        queue = newQueue;
    }
    theBack = (theBack+1)%arrayLength;
    queue[theBack] = theElement;
}

#endif //ALGORITHMS_ARRAYQUEUE_H
